/*
 * @Author: master
 * @Date: 2022-01-08 12:44:48
 * @LastEditTime: 2022-06-28 15:32:20
 * @Description: node服务工具
 */
const fs = require('fs');
const os = require('os');
const path = require('path');
const JsZip = require('jszip'); // 压缩服务
/**
 * 获取未暂用的端口
 * @returns port
 */
exports.getPort = async () => {
  const portfinder = require('portfinder');
  portfinder.basePort = 9000;
  return await portfinder
    .getPortPromise()
    .then(port => {
      return port;
    })
    .catch(err => {
      console.log(err);
      return 9091;
    });
};
/**
 * 获取本机IP地址
 */
exports.getIPAddress = () => {
  const interfaces = os.networkInterfaces();
  for (const devName in interfaces) {
    const iface = interfaces[devName];
    for (let i = 0; i < iface.length; i++) {
      const alias = iface[i];
      if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
        return alias.address;
      }
    }
  }
};
/**
 * 获取所有项目地址
 * @param {*} projectPath 存放项目的目录
 * @returns {path,folder} 返回当前路径和目录
 */
exports.getAllProPath = projectPath => {
  // 获取子项目名称和地址
  const folders = fs
    .readdirSync(projectPath)
    .map(folder => {
      // 当前项目路径
      const localPath = path.join(projectPath, folder);
      const folderSta = fs.statSync(localPath);
      if (folderSta.isDirectory()) {
        return {
          path: localPath,
          name: folder,
        };
      }
    })
    .filter(folder => folder);
  return folders;
};
/**
 * 输出不同颜色
 * @param color 颜色键名
 * @returns {*}
 */
const colors = color => {
  const colorSet = {
    bright: '\x1B[1m', // 亮色
    grey: '\x1B[2m', // 灰色
    italic: '\x1B[3m', // 斜体
    underline: '\x1B[4m', // 下划线
    reverse: '\x1B[7m', // 反向
    hidden: '\x1B[8m', // 隐藏
    black: '\x1B[30m', // 黑色
    red: '\x1B[31m', // 红色
    green: '\x1B[32m', // 绿色
    yellow: '\x1B[33m', // 黄色
    blue: '\x1B[34m', // 蓝色
    magenta: '\x1B[35m', // 品红
    cyan: '\x1B[36m', // 青色
    white: '\x1B[37m', // 白色
    blackBG: '\x1B[40m', // 背景色为黑色
    redBG: '\x1B[41m', // 背景色为红色
    greenBG: '\x1B[42m', // 背景色为绿色
    yellowBG: '\x1B[43m', // 背景色为黄色
    blueBG: '\x1B[44m', // 背景色为蓝色
    magentaBG: '\x1B[45m', // 背景色为品红
    cyanBG: '\x1B[46m', // 背景色为青色
    whiteBG: '\x1B[47m', // 背景色为白色
  };
  return colorSet[color];
};
exports.colors = colors;
/**
 * 根据路径获取当前的目录详情
 * folderPath: 绝对目录地址
 */
exports.getParseUrl = folderPath => {
  return path.parse(folderPath);
};
/**
 * 判断每个项目是否包含必须的index.html和index.js
 */
exports.isIndexHtmlJs = proPath => {
  const files = fs
    .readdirSync(proPath)
    .map(file => {
      // 当前项目路径
      const localPath = path.join(proPath, file);
      const folderSta = fs.statSync(localPath);
      if (!folderSta.isDirectory()) {
        return file;
      }
    })
    .filter(file => file);
  return (files.includes('index.js') || files.includes('index.ts')) && files.includes('index.html');
};
/**
 * 判断文件是否存在
 */
exports.isFile = file => {
  return fs.existsSync(file);
};
/**
 * 复制文件
 * @param {*} srcDir 要拷贝的文件夹
 * @param {*} tarDir 目标文件夹
 * @param {*} cb 回调
 */
const copyFile = (srcPath, tarPath, cb) => {
  const rs = fs.createReadStream(srcPath);
  rs.on('error', function (err) {
    if (err) {
      console.log('read error', srcPath);
    }
    cb && cb(err);
  });

  const ws = fs.createWriteStream(tarPath);
  ws.on('error', function (err) {
    if (err) {
      console.log('write error', tarPath);
    }
    cb && cb(err);
  });

  ws.on('close', function (ex) {
    cb && cb(ex);
  });

  rs.pipe(ws);
};
exports.copyFile = copyFile;
/**
 * 拷贝文件夹内容到另一个文件夹内
 * @param {*} srcDir 要拷贝的文件夹
 * @param {*} tarDir 目标文件夹
 * @param {*} cb 回调
 */
const copySrcDirToTarDir = (srcDir, tarDir, cb) => {
  if (fs.existsSync(tarDir)) {
    fs.readdir(srcDir, (err, files) => {
      try {
        let count = 0;
        const checkEnd = () => {
          ++count === files.length && cb && cb();
        };
        if (err) {
          checkEnd();
          return;
        }
        files.forEach(file => {
          const srcPath = path.join(srcDir, file);
          const tarPath = path.join(tarDir, file);
          fs.stat(srcPath, function (_err, stats) {
            if (stats.isDirectory()) {
              fs.mkdir(tarPath, function (err) {
                if (err) {
                  return;
                }
                copySrcDirToTarDir(srcPath, tarPath, checkEnd);
              });
            } else {
              copyFile(srcPath, tarPath, checkEnd);
            }
          });
        });
        // 为空时直接回调
        files.length === 0 && cb && cb();
      } catch (e) {
        console.log('异常了');
      }
    });
  } else {
    console.log(colors('yellow'), '警告：目标文件夹不存在！');
  }
};
exports.copySrcDirToTarDir = copySrcDirToTarDir;
/**
 * 删除目录内的所有文件
 */
const removeDir = dirPath => {
  const statObj = fs.statSync(dirPath); // fs.statSync同步读取文件状态，判断是文件目录还是文件。
  if (statObj.isDirectory()) {
    // 如果是目录
    let dirs = fs.readdirSync(dirPath); // fs.readdirSync()同步的读取目标下的文件 返回一个不包括 '.' 和 '..' 的文件名的数组['b','a']
    dirs = dirs.map(dir => path.join(dirPath, dir)); // 拼上完整的路径
    for (let i = 0; i < dirs.length; i++) {
      // 深度 先将儿子移除掉 再删除掉自己
      removeDir(dirs[i]);
    }
    fs.rmdirSync(dirPath); // 删除目录
  } else {
    fs.unlinkSync(dirPath); // 删除文件
  }
};
// 目录删除
exports.removeDir = removeDir;
/**
 * 同步创建文件夹（目录）
 */
exports.mkdirDirectory = path => {
  fs.mkdirSync(path);
};
/**
 * 文件夹压缩服务
 * @param folderPath 压缩文件夹路径
 * @param ptName 压缩完成之后的文件别名
 * @param callBack 压缩完成回调
 */
exports.zipFolderServer = (folderPath, ptName = 'dist.zip', callBack) => {
  const zip = new JsZip();
  /**
   * 读取文件夹并且开始压缩
   */
  const readDir = (zip, dirPath) => {
    // 读取dist下的根文件目录
    const files = fs.readdirSync(dirPath);
    files.forEach(fileName => {
      const fillPath = dirPath + '/' + fileName;
      const file = fs.statSync(fillPath);
      // 如果是文件夹的话需要递归遍历下面的子文件
      if (file.isDirectory()) {
        const dirZip = zip.folder(fileName);
        readDir(dirZip, fillPath);
      } else {
        // 读取每个文件为buffer存到zip中
        zip.file(fileName, fs.readFileSync(fillPath));
      }
    });
  };
  // 读取文件夹并且开始压缩
  readDir(zip, folderPath);
  zip
    .generateAsync({
      type: 'nodebuffer', // 压缩类型
      compression: 'DEFLATE', // 压缩算法
      compressionOptions: {
        // 压缩级别
        level: 9,
      },
    })
    .then(function (content) {
      const distZip = path.resolve(__dirname, folderPath, '../', ptName);
      if (fs.existsSync(distZip)) {
        // 删除旧包
        fs.unlinkSync(distZip);
      }
      // 把zip包写到硬盘中，这个content现在是一段buffer
      fs.writeFileSync(distZip, content);
      if (callBack) callBack();
    });
};
